package geektime.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;

/**
 * 模拟服务器
 */
public class EchoServer {

    private static final String SENSITIVE = "sensitive";

    public static void main(String[] args) {
        try {
            // 创建一个selector和serverSocketChannel
            Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            // 给serverSocketChannel绑定一个端口：1234
            serverSocketChannel.bind(new InetSocketAddress("localhost",1234));
            // 将serverSocketChannel设置为非阻塞式
            serverSocketChannel.configureBlocking(false);
            // 将serverSocketChannel注册到selector中，并说明其关注点是连接请求
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            // 创建一个ByteBuffer，并分配256个字节
            ByteBuffer buffer = ByteBuffer.allocate(256);
            System.out.println("Server start writing...");
            while (true) {
                // 这一步用于阻塞等待就绪的channel
                selector.select();
                // 获取selector下的selected-set
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    // 判断当前的selectionKey是否准备好接收一个新的socket。如果是，则生成clientSocketChannel并注册到selector
                    if (selectionKey.isAcceptable()) {
                        clientSocketRegister(selector, serverSocketChannel);
                    }
                    // 判断当前的selectionKey是否准备好读操作。如果是，则对clientSocket中的内容进行处理
                    if (selectionKey.isReadable()) {
                        handleClient(buffer, selectionKey);
                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handleClient(ByteBuffer buffer, SelectionKey selectionKey) throws IOException {
        // 通过selectionKey返回clientSocketChannel，并将内容读取到ByteBuffer中
        SocketChannel clientSocketChannel = (SocketChannel) selectionKey.channel();
        clientSocketChannel.read(buffer);
        // 此方法会获取到buffer中所有的数据，不建议使用
//        String message = new String(buffer.array()).trim();
        // 此方法可以获取buffer中从0到position的内容
        String message = getCurrentTimesMessage(buffer);
        if (SENSITIVE.equals(message)) {
            // 如果客户端发送过来的信息是sensitive，则打印一个错误提示结果
            System.out.println("message from client include sensitive");
            buffer.clear();
        } else {
            // 打印buffer中的内容
            System.out.println("Received message: " + message);
            // 由于接下来需要重新写入内容，所以需要clear()
            buffer.clear();
            buffer.put(("finished " + message).getBytes());
            // 将buffer中的内容写入到clientSocketChannel前需要进行flip()操作
            buffer.flip();
            clientSocketChannel.write(buffer);
            // 清理buffer中的内容（将position置为0，将limit置为capacity），为下一次接收内容做好准备
            buffer.clear();
        }
    }

    private static String getCurrentTimesMessage(ByteBuffer buffer) {
        byte[] bytes = new byte[buffer.position()];
        buffer.flip();
        int i = 0;
        while (buffer.hasRemaining()) {
            bytes[i++] = buffer.get();
        }
        return new String(bytes);
    }

    private static void clientSocketRegister(Selector selector, ServerSocketChannel serverSocketChannel) throws IOException {
        // 如果是，则接收连接并生成clientSocketChannel
        SocketChannel clientSocketChannel = serverSocketChannel.accept();
        // 将clientSocketChannel注册到selector中，并说明其关注点是读和写
        clientSocketChannel.configureBlocking(false);
        clientSocketChannel.register(selector, SelectionKey.OP_READ);
    }
}
